spb/autoscholar
Public
1import { AppError, notFound } from "@autoscholar/core";2import { artifacts } from "@autoscholar/db";3import { and, desc, eq } from "drizzle-orm";4import { handler } from "@/lib/server/api";5import { db, storage } from "@/lib/server/db";6import { requireAdmin } from "@/lib/server/session";78export const runtime = "nodejs";910/** Private artifact download for admins (latest version unless ?version=n). */11export const GET = handler(async (req, { params }) => {12 await requireAdmin(req);13 const { id, path } = (await params) as { id: string; path: string[] };14 const rel = path.map(decodeURIComponent).join("/");15 if (rel.includes("..")) throw new AppError("invalid", "bad path", 400);16 const url = new URL(req.url);17 const version = url.searchParams.get("version");18 const conds = [eq(artifacts.runId, id), eq(artifacts.path, rel)];19 if (version) conds.push(eq(artifacts.version, Number(version)));20 const [row] = await db()21 .select()22 .from(artifacts)23 .where(and(...conds))24 .orderBy(desc(artifacts.version))25 .limit(1);26 if (!row) throw notFound("artifact");27 const buf = await storage().get(row.storageKey);28 const inline =29 /^(application\/pdf|image\/|text\/)/.test(row.contentType) && url.searchParams.get("download") !== "1";30 return new Response(new Uint8Array(buf), {31 headers: {32 "content-type": row.contentType,33 "content-length": String(buf.length),34 "content-disposition": `${inline ? "inline" : "attachment"}; filename="${rel.split("/").pop()}"`,35 "x-artifact-sha256": row.sha256,36 "cache-control": "private, no-store",37 "content-security-policy": "sandbox",38 },39 });40});41